/* File name    :  nfivi_map.c
 * Author       :  Wentao Shang
 *
 * Contents     :
 *    This file defines the basic mapping list operations and and some important
 *    global variables, which will be used in other modules. 'nfivi_map' module
 *    will be installed first when running './control start' command.
 *
 */

#include "nfivi_map.h"

/* maxmum time for a map to exist */
time_t TimeOut = 240;  // counted in seconds
EXPORT_SYMBOL(TimeOut);

/* Mod and Res together indicate the port pool range */
unsigned int Mod = 256;
EXPORT_SYMBOL(Mod);

unsigned int Res = 1;
EXPORT_SYMBOL(Res);

/* storing the last assigned port */
unsigned int Current = 0;
EXPORT_SYMBOL(Current);

/* storing the map tuple */
struct map_list MapList;
EXPORT_SYMBOL(MapList);

/* flag for Mod/Res auto-configuration */
int AutoConfig = 1;
EXPORT_SYMBOL(AutoConfig);

/* list operations */

// 初始化链表
void init_map_list(struct map_list *list)
{
	spin_lock_init(&list->lock);
	INIT_LIST_HEAD(&list->chain);
}
EXPORT_SYMBOL(init_map_list);

// 返回链表长度
unsigned int get_list_size(struct map_list *list)
{
	unsigned int size = 0;
	struct list_head *p;
	spin_lock(&list->lock);
	list_for_each(p, &list->chain) {
		size++;
	}
	spin_unlock(&list->lock);
	return size;
}

// 检查端口是否正被使用
int port_in_use(unsigned int port, struct map_list *list)
{
	struct map_tuple *iter;
	int ret;
	
	ret = 0;
	spin_lock(&list->lock);
	list_for_each_entry(iter, &list->chain, node) {
		if( iter->newport == port ) {
			ret = 1;
			break;
		}
	}
	spin_unlock(&list->lock);
	
	return ret;
}

// 添加新的端口映射项，传入的端口号以主机字节序存放！返回指向该项的指针
struct map_tuple* add_new_map(unsigned short oldp, unsigned short newp, struct map_list *list)
{
	struct map_tuple *map;
	map = (struct map_tuple*)kmalloc( sizeof(struct map_tuple), GFP_KERNEL );
	if( map == NULL ) {
		printk("add_new_map: kmalloc failed!\n");
		return NULL;
	}
	
	map->oldport = oldp;
	map->newport = newp;
	do_gettimeofday(&map->timer);
	
	spin_lock(&list->lock);
	list_add(&map->node, &list->chain);
	spin_unlock(&list->lock);
	printk("New Port Map Added: %d -> %d\n", oldp, newp );
	return map;
}

// 检查定时器并刷新
void refresh_map_list(struct map_list *list)
{
	struct map_tuple *iter;
	struct map_tuple *temp;
	struct timeval now;
	time_t delta;
	do_gettimeofday(&now);
	
	spin_lock(&list->lock);
	list_for_each_entry_safe(iter, temp, &list->chain, node) {
		delta = now.tv_sec - iter->timer.tv_sec;
		if( delta >= TimeOut ) {
			list_del(&iter->node);
			printk("Map %d -> %d time out!\n", iter->oldport, iter->newport);
			kfree(iter);
		}
	}
	spin_unlock(&list->lock);
}
EXPORT_SYMBOL(refresh_map_list);

// 清空整个链表
void free_map_list(struct map_list *list)
{
	struct map_tuple *iter;
	struct map_tuple *temp;
	
	spin_lock(&list->lock);
	list_for_each_entry_safe(iter, temp, &list->chain, node) {
		list_del(&iter->node);
		printk("Map %d -> %d deleted!\n", iter->oldport, iter->newport);
		kfree(iter);
	}
	spin_unlock(&list->lock);
}
EXPORT_SYMBOL(free_map_list);

/* mapping operations */

// 获取流出数据包的端口映射，传入及返回的端口号均为主机字节序！失败返回0
unsigned short get_outflow_map_port(unsigned short oldp, struct map_list *list)
{
	unsigned short retport;
	
	refresh_map_list(list);
	
	if( get_list_size(list) >= (int)(65536 / Mod) ) {
		printk("Map list full!\n");
		return 0;
	}
	
	retport = 0;
	
	if( !list_empty(&list->chain) ) {
		struct map_tuple *iter;
		spin_lock(&list->lock);
		list_for_each_entry(iter, &list->chain, node) {
			if( iter->oldport == oldp ) {
				retport = iter->newport;
				do_gettimeofday(&iter->timer);
				printk("Find map %d -> %d\n", oldp, retport);
				break;
			}
		}
		spin_unlock(&list->lock);
	}
	
	if( retport == 0 ) {
		// add algorithm here!
		int remaining, rover, low, high;
		
		low = (int)(1024 / Mod) + 1;
		high = (int)(65536 / Mod) - 1;
		remaining = (high - low) + 1;
		
		if( Current != 0 )
			rover = Current+1;
		else
			rover = low;
		
		do{ 
			retport = rover * Mod + Res;
			if( !port_in_use(retport, list) )
				break;
			
			if( ++rover > high )
				rover = low;
			
		}
		while( --remaining > 0 );
		
		if( remaining <= 0 ) {
			printk("Failed to assign a new map port for port: %d\n", oldp);
			return 0;
		}
		
		Current = rover;
		
		if( add_new_map( oldp, retport, list ) == NULL )
			return 0;
	}
	
	return retport;
}
EXPORT_SYMBOL(get_outflow_map_port);

// 查找流入数据包的端口映射，传入及返回的端口号均为主机字节序！失败返回0
unsigned short get_inflow_map_port(unsigned short newp, struct map_list *list)
{
	unsigned short retport;
	struct map_tuple *iter;
	
	refresh_map_list(list);
	
	if( list_empty(&list->chain) ) {
		printk("Map list empty!\n");
		return 0;
	}

	retport = 0;
	spin_lock(&list->lock);
	list_for_each_entry(iter, &list->chain, node) {
		if( iter->newport == newp ) {
			retport = iter->oldport;
			do_gettimeofday(&iter->timer);
			printk("Find map %d -> %d\n", retport, newp);
			break;
		}
	}
	spin_unlock(&list->lock);
	return retport;
}
EXPORT_SYMBOL(get_inflow_map_port);

MODULE_LICENSE("GPL");

